chore: improve error messages (#482)

Improve error messages so we don't just get TypeError with no clue what
the issue was.
This commit is contained in:
Steven Hartland
2022-12-07 00:13:42 +00:00
committed by GitHub
parent 7927fb4a26
commit ddcbf14a26
26 changed files with 173 additions and 143 deletions

View File

@@ -25,6 +25,7 @@ jobs:
- name: Validate go mod / generate
run: |
go mod tidy
go install golang.org/x/tools/cmd/stringer@latest
go generate ./...
git --no-pager diff && [[ 0 -eq $(git status --porcelain | wc -l) ]]

View File

@@ -111,7 +111,7 @@ func TestArray_toLocaleString(t *testing.T) {
test(`raise:
[ { toLocaleString: undefined } ].toLocaleString();
`, "TypeError")
`, `TypeError: Array.toLocaleString index[0] "undefined" is not callable`)
})
}
@@ -429,9 +429,9 @@ func TestArray_every(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: [].every()`, "TypeError")
test(`raise: [].every()`, `TypeError: Array.every argument "undefined" is not callable`)
test(`raise: [].every("abc")`, "TypeError")
test(`raise: [].every("abc")`, `TypeError: Array.every argument "abc" is not callable`)
test(`[].every(function() { return false })`, true)
@@ -479,7 +479,7 @@ func TestArray_some(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: [].some("abc")`, "TypeError")
test(`raise: [].some("abc")`, `TypeError: Array.some "abc" if not callable`)
test(`[].some(function() { return true })`, false)
@@ -521,7 +521,7 @@ func TestArray_forEach(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: [].forEach("abc")`, "TypeError")
test(`raise: [].forEach("abc")`, `TypeError: Array.foreach "abc" if not callable`)
test(`
var abc = 0;
@@ -594,7 +594,7 @@ func TestArray_map(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: [].map("abc")`, "TypeError")
test(`raise: [].map("abc")`, `TypeError: Array.foreach "abc" if not callable`)
test(`[].map(function() { return 1 }).length`, 0)
@@ -626,7 +626,7 @@ func TestArray_filter(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: [].filter("abc")`, "TypeError")
test(`raise: [].filter("abc")`, `TypeError: Array.filter "abc" if not callable`)
test(`[].filter(function() { return 1 }).length`, 0)
@@ -640,9 +640,9 @@ func TestArray_reduce(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: [].reduce("abc")`, "TypeError")
test(`raise: [].reduce("abc")`, `TypeError: Array.reduce "abc" if not callable`)
test(`raise: [].reduce(function() {})`, "TypeError")
test(`raise: [].reduce(function() {})`, `TypeError: Array.reduce "function() {}" if not callable`)
test(`[].reduce(function() {}, 0)`, 0)
@@ -660,9 +660,9 @@ func TestArray_reduceRight(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: [].reduceRight("abc")`, "TypeError")
test(`raise: [].reduceRight("abc")`, `TypeError: Array.reduceRight "abc" if not callable`)
test(`raise: [].reduceRight(function() {})`, "TypeError")
test(`raise: [].reduceRight(function() {})`, `TypeError: Array.reduceRight "function() {}" if not callable`)
test(`[].reduceRight(function() {}, 0)`, 0)
@@ -697,7 +697,7 @@ func TestArray_defineOwnProperty(t *testing.T) {
Object.defineProperty(abc, "length", {
writable: true
});
`, "TypeError")
`, `TypeError: Object.DefineOwnProperty: property not configurable or writeable and descriptor not writeable`)
})
}

View File

@@ -52,7 +52,7 @@ func builtinArrayToLocaleString(call FunctionCall) Value {
obj := call.runtime.toObject(value)
toLocaleString := obj.get("toLocaleString")
if !toLocaleString.isCallable() {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Array.toLocaleString index[%d] %q is not callable", index, toLocaleString))
}
stringValue = toLocaleString.call(call.runtime, objectValue(obj)).string()
}
@@ -457,7 +457,7 @@ func builtinArraySort(call FunctionCall) Value {
compare := compareValue.object()
if compareValue.IsUndefined() {
} else if !compareValue.isCallable() {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Array.sort value %q is not callable", compareValue))
}
if length > 1 {
arraySortQuickSort(thisObject, 0, length-1, compare)
@@ -541,7 +541,7 @@ func builtinArrayEvery(call FunctionCall) Value {
}
return trueValue
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Array.every argument %q is not callable", call.Argument(0)))
}
func builtinArraySome(call FunctionCall) Value {
@@ -559,7 +559,7 @@ func builtinArraySome(call FunctionCall) Value {
}
return falseValue
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Array.some %q if not callable", call.Argument(0)))
}
func builtinArrayForEach(call FunctionCall) Value {
@@ -575,7 +575,7 @@ func builtinArrayForEach(call FunctionCall) Value {
}
return Value{}
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Array.foreach %q if not callable", call.Argument(0)))
}
func builtinArrayMap(call FunctionCall) Value {
@@ -594,7 +594,7 @@ func builtinArrayMap(call FunctionCall) Value {
}
return objectValue(call.runtime.newArrayOf(values))
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Array.foreach %q if not callable", call.Argument(0)))
}
func builtinArrayFilter(call FunctionCall) Value {
@@ -614,7 +614,7 @@ func builtinArrayFilter(call FunctionCall) Value {
}
return objectValue(call.runtime.newArrayOf(values))
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Array.filter %q if not callable", call.Argument(0)))
}
func builtinArrayReduce(call FunctionCall) Value {
@@ -647,7 +647,7 @@ func builtinArrayReduce(call FunctionCall) Value {
return accumulator
}
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Array.reduce %q if not callable", call.Argument(0)))
}
func builtinArrayReduceRight(call FunctionCall) Value {
@@ -679,5 +679,5 @@ func builtinArrayReduceRight(call FunctionCall) Value {
return accumulator
}
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Array.reduceRight %q if not callable", call.Argument(0)))
}

View File

@@ -81,7 +81,7 @@ func builtinDateToJSON(call FunctionCall) Value {
toISOString := obj.get("toISOString")
if !toISOString.isCallable() {
// FIXME
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Date.toJSON toISOString %q is not callable", toISOString))
}
return toISOString.call(call.runtime, objectValue(obj), []Value{})
}

View File

@@ -15,7 +15,7 @@ func builtinNewError(obj *object, argumentList []Value) Value {
func builtinErrorToString(call FunctionCall) Value {
thisObject := call.thisObject()
if thisObject == nil {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Error.toString is nil"))
}
name := classErrorName

View File

@@ -58,14 +58,14 @@ func builtinFunctionToString(call FunctionCall) Value {
return stringValue(fn.node.source)
case bindFunctionObject:
return stringValue("function () { [native code] }")
default:
panic(call.runtime.panicTypeError("Function.toString unknown type %T", obj.value))
}
panic(call.runtime.panicTypeError("Function.toString()"))
}
func builtinFunctionApply(call FunctionCall) Value {
if !call.This.isCallable() {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Function.apply %q is not callable", call.This))
}
this := call.Argument(0)
if this.IsUndefined() {
@@ -78,7 +78,7 @@ func builtinFunctionApply(call FunctionCall) Value {
return call.thisObject().call(this, nil, false, nativeFrame)
case valueObject:
default:
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Function.apply unknown type %T for second argument"))
}
arrayObject := argumentList.object()
@@ -93,7 +93,7 @@ func builtinFunctionApply(call FunctionCall) Value {
func builtinFunctionCall(call FunctionCall) Value {
if !call.This.isCallable() {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Function.call %q is not callable", call.This))
}
thisObject := call.thisObject()
this := call.Argument(0)
@@ -110,7 +110,7 @@ func builtinFunctionCall(call FunctionCall) Value {
func builtinFunctionBind(call FunctionCall) Value {
target := call.This
if !target.isCallable() {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Function.bind %q is not callable", call.This))
}
targetObject := target.object()

View File

@@ -180,12 +180,12 @@ func builtinJSONStringify(call FunctionCall) Value {
}
valueJSON, err := json.Marshal(value)
if err != nil {
panic(call.runtime.panicTypeError(err.Error()))
panic(call.runtime.panicTypeError("JSON.stringify marshal: %s", err))
}
if ctx.gap != "" {
valueJSON1 := bytes.Buffer{}
if err = json.Indent(&valueJSON1, valueJSON, "", ctx.gap); err != nil {
panic(call.runtime.panicTypeError(err.Error()))
panic(call.runtime.panicTypeError("JSON.stringify indent: %s", err))
}
valueJSON = valueJSON1.Bytes()
}

View File

@@ -81,7 +81,7 @@ func builtinObjectToString(call FunctionCall) Value {
func builtinObjectToLocaleString(call FunctionCall) Value {
toString := call.thisObject().get("toString")
if !toString.isCallable() {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.toLocaleString %q is not callable", toString))
}
return toString.call(call.runtime, call.This)
}
@@ -90,7 +90,7 @@ func builtinObjectGetPrototypeOf(call FunctionCall) Value {
val := call.Argument(0)
obj := val.object()
if obj == nil {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.GetPrototypeOf is nil"))
}
if obj.prototype == nil {
@@ -104,7 +104,7 @@ func builtinObjectGetOwnPropertyDescriptor(call FunctionCall) Value {
val := call.Argument(0)
obj := val.object()
if obj == nil {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.GetOwnPropertyDescriptor is nil"))
}
name := call.Argument(1).string()
@@ -119,7 +119,7 @@ func builtinObjectDefineProperty(call FunctionCall) Value {
val := call.Argument(0)
obj := val.object()
if obj == nil {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.DefineProperty is nil"))
}
name := call.Argument(1).string()
descriptor := toPropertyDescriptor(call.runtime, call.Argument(2))
@@ -131,7 +131,7 @@ func builtinObjectDefineProperties(call FunctionCall) Value {
val := call.Argument(0)
obj := val.object()
if obj == nil {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.DefineProperties is nil"))
}
properties := call.runtime.toObject(call.Argument(1))
@@ -147,7 +147,7 @@ func builtinObjectDefineProperties(call FunctionCall) Value {
func builtinObjectCreate(call FunctionCall) Value {
prototypeValue := call.Argument(0)
if !prototypeValue.IsNull() && !prototypeValue.IsObject() {
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.Create is nil"))
}
obj := call.runtime.newObject()
@@ -171,17 +171,16 @@ func builtinObjectIsExtensible(call FunctionCall) Value {
if obj := val.object(); obj != nil {
return boolValue(obj.extensible)
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.IsExtensible is nil"))
}
func builtinObjectPreventExtensions(call FunctionCall) Value {
val := call.Argument(0)
if obj := val.object(); obj != nil {
obj.extensible = false
} else {
panic(call.runtime.panicTypeError())
return val
}
return val
panic(call.runtime.panicTypeError("Object.PreventExtensions is nil"))
}
func builtinObjectIsSealed(call FunctionCall) Value {
@@ -200,7 +199,7 @@ func builtinObjectIsSealed(call FunctionCall) Value {
})
return boolValue(result)
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.IsSealed is nil"))
}
func builtinObjectSeal(call FunctionCall) Value {
@@ -214,10 +213,9 @@ func builtinObjectSeal(call FunctionCall) Value {
return true
})
obj.extensible = false
} else {
panic(call.runtime.panicTypeError())
return val
}
return val
panic(call.runtime.panicTypeError("Object.Seal is nil"))
}
func builtinObjectIsFrozen(call FunctionCall) Value {
@@ -236,7 +234,7 @@ func builtinObjectIsFrozen(call FunctionCall) Value {
})
return boolValue(result)
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.IsFrozen is nil"))
}
func builtinObjectFreeze(call FunctionCall) Value {
@@ -259,10 +257,9 @@ func builtinObjectFreeze(call FunctionCall) Value {
return true
})
obj.extensible = false
} else {
panic(call.runtime.panicTypeError())
return val
}
return val
panic(call.runtime.panicTypeError("Object.Freeze is nil"))
}
func builtinObjectKeys(call FunctionCall) Value {
@@ -273,7 +270,7 @@ func builtinObjectKeys(call FunctionCall) Value {
})
return objectValue(call.runtime.newArrayOf(keys))
}
panic(call.runtime.panicTypeError())
panic(call.runtime.panicTypeError("Object.Keys is nil"))
}
func builtinObjectGetOwnPropertyNames(call FunctionCall) Value {

View File

@@ -169,7 +169,7 @@ func (rt *runtime) cmplEvaluateNodeBracketExpression(node *nodeBracketExpression
// TODO Pass in base value as-is, and defer toObject till later?
obj, err := rt.objectCoerce(targetValue)
if err != nil {
panic(rt.panicTypeError("Cannot access member '%s' of %s", memberValue.string(), err.Error(), at(node.idx)))
panic(rt.panicTypeError("Cannot access member %q of %s", memberValue.string(), err, at(node.idx)))
}
return toValue(newPropertyReference(rt, obj, memberValue.string(), false, at(node.idx)))
}
@@ -201,7 +201,7 @@ func (rt *runtime) cmplEvaluateNodeCallExpression(node *nodeCallExpression, with
eval = rf.name == "eval" // Possible direct eval
default:
// FIXME?
panic(rt.panicTypeError("Here be dragons"))
panic(rt.panicTypeError("unexpected callee type %T to node call expression", rf))
}
}
@@ -226,7 +226,7 @@ func (rt *runtime) cmplEvaluateNodeCallExpression(node *nodeCallExpression, with
// FIXME Maybe typeof?
panic(rt.panicTypeError("%v is not a function", vl, atv))
}
panic(rt.panicTypeError("'%s' is not a function", name, atv))
panic(rt.panicTypeError("%q is not a function", name, atv))
}
rt.scope.frame.offset = int(atv)
@@ -249,7 +249,7 @@ func (rt *runtime) cmplEvaluateNodeDotExpression(node *nodeDotExpression) Value
// TODO Pass in base value as-is, and defer toObject till later?
obj, err := rt.objectCoerce(targetValue)
if err != nil {
panic(rt.panicTypeError("Cannot access member '%s' of %s", node.identifier, err.Error(), at(node.idx)))
panic(rt.panicTypeError("Cannot access member %q of %s", node.identifier, err, at(node.idx)))
}
return toValue(newPropertyReference(rt, obj, node.identifier, false, at(node.idx)))
}
@@ -270,7 +270,7 @@ func (rt *runtime) cmplEvaluateNodeNewExpression(node *nodeNewExpression) Value
case *stashReference:
name = rf.name
default:
panic(rt.panicTypeError("Here be dragons"))
panic(rt.panicTypeError("node new expression unexpected callee type %T", rf))
}
}

View File

@@ -80,7 +80,7 @@ func TestErrorContext(t *testing.T) {
`)
{
err := asError(t, err)
is(err.message, "'undefined' is not a function")
is(err.message, `"undefined" is not a function`)
is(len(err.trace), 1)
is(err.trace[0].location(), "<anonymous>:2:13")
}
@@ -90,7 +90,7 @@ func TestErrorContext(t *testing.T) {
`)
{
err := asError(t, err)
is(err.message, "'abc' is not a function")
is(err.message, `"abc" is not a function`)
is(len(err.trace), 1)
is(err.trace[0].location(), "<anonymous>:2:14")
}
@@ -100,7 +100,7 @@ func TestErrorContext(t *testing.T) {
`)
{
err := asError(t, err)
is(err.message, "'abc' is not a function")
is(err.message, `"abc" is not a function`)
is(len(err.trace), 1)
is(err.trace[0].location(), "<anonymous>:2:14")
}
@@ -111,7 +111,7 @@ func TestErrorContext(t *testing.T) {
`)
{
err := asError(t, err)
is(err.message, "'ghi' is not a function")
is(err.message, `"ghi" is not a function`)
is(len(err.trace), 1)
is(err.trace[0].location(), "<anonymous>:3:13")
}
@@ -127,7 +127,7 @@ func TestErrorContext(t *testing.T) {
`)
{
err := asError(t, err)
is(err.message, "'undefined' is not a function")
is(err.message, `"undefined" is not a function`)
is(len(err.trace), 3)
is(err.trace[0].location(), "def (<anonymous>:3:17)")
is(err.trace[1].location(), "abc (<anonymous>:6:17)")
@@ -174,7 +174,7 @@ func TestErrorContext(t *testing.T) {
`)
{
err := asError(t, err)
is(err.message, "'xyzzy' is not a function")
is(err.message, `"xyzzy" is not a function`)
is(len(err.trace), 1)
is(err.trace[0].location(), "<anonymous>:1:1")
}
@@ -251,7 +251,7 @@ func TestErrorContext(t *testing.T) {
f, _ := vm.Get("C")
_, err := f.Call(UndefinedValue())
err1 := asError(t, err)
is(err1.message, "Cannot access member 'prop' of null")
is(err1.message, `Cannot access member "prop" of null`)
is(len(err1.trace), 1)
is(err1.trace[0].location(), "C (file1.js:7:5)")
}

View File

@@ -119,14 +119,14 @@ func (rt *runtime) calculateBinaryExpression(operator token.Token, left Value, r
case token.INSTANCEOF:
rightValue := right.resolve()
if !rightValue.IsObject() {
panic(rt.panicTypeError("Expecting a function in instanceof check, but got: %v", rightValue))
panic(rt.panicTypeError("invalid kind %s for instanceof (expected object)", rightValue.kind))
}
return boolValue(rightValue.object().hasInstance(leftValue))
case token.IN:
rightValue := right.resolve()
if !rightValue.IsObject() {
panic(rt.panicTypeError())
panic(rt.panicTypeError("invalid kind %s for in (expected object)", rightValue.kind))
}
return boolValue(rightValue.object().hasProperty(leftValue.string()))
}

View File

@@ -229,7 +229,7 @@ func TestFunction_bind(t *testing.T) {
test(`raise:
Math.bind();
`, "TypeError: 'bind' is not a function")
`, `TypeError: "bind" is not a function`)
test(`
function construct(fn, arguments) {
@@ -268,7 +268,7 @@ func TestFunction_toString(t *testing.T) {
test(`raise:
Function.prototype.toString.call(undefined);
`, "TypeError")
`, "TypeError: Function.Class environment != Function")
test(`
abc = function() { return -1 ;

View File

@@ -1,3 +1,4 @@
package otto
//go:generate go run ./tools/gen-jscore -output inline.go
//go:generate stringer -type=valueKind -trimprefix=value -output=value_kind.gen.go

View File

@@ -210,7 +210,7 @@ func objectCanPutDetails(obj *object, name string) (canPut bool, prop *property,
canPut = setter != nil
return
default:
panic(obj.runtime.panicTypeError())
panic(obj.runtime.panicTypeError("unexpected type %T to Object.CanPutDetails", prop.value))
}
}
@@ -234,7 +234,7 @@ func objectCanPutDetails(obj *object, name string) (canPut bool, prop *property,
canPut = setter != nil
return
default:
panic(obj.runtime.panicTypeError())
panic(obj.runtime.panicTypeError("unexpected type %T to Object.CanPutDetails", prop.value))
}
}
@@ -282,22 +282,23 @@ func objectPut(obj *object, name string, value Value, throw bool) {
}
}
obj.defineProperty(name, value, 0o111, throw)
} else {
switch propertyValue := prop.value.(type) {
case Value:
prop.value = value
obj.defineOwnProperty(name, *prop, throw)
case propertyGetSet:
if propertyValue[1] != nil {
propertyValue[1].call(toValue(obj), []Value{value}, false, nativeFrame)
return
}
if throw {
panic(obj.runtime.panicTypeError())
}
default:
panic(obj.runtime.panicTypeError())
return
}
switch propertyValue := prop.value.(type) {
case Value:
prop.value = value
obj.defineOwnProperty(name, *prop, throw)
case propertyGetSet:
if propertyValue[1] != nil {
propertyValue[1].call(toValue(obj), []Value{value}, false, nativeFrame)
return
}
if throw {
panic(obj.runtime.panicTypeError("Object.Put nil second parameter to propertyGetSet"))
}
default:
panic(obj.runtime.panicTypeError("Object.Put unexpected type %T", prop.value))
}
}
@@ -312,9 +313,9 @@ func objectHasOwnProperty(obj *object, name string) bool {
// 8.12.9.
func objectDefineOwnProperty(obj *object, name string, descriptor property, throw bool) bool {
reject := func() bool {
reject := func(reason string) bool {
if throw {
panic(obj.runtime.panicTypeError())
panic(obj.runtime.panicTypeError("Object.DefineOwnProperty: %s", reason))
}
return false
}
@@ -322,7 +323,7 @@ func objectDefineOwnProperty(obj *object, name string, descriptor property, thro
prop, exists := obj.readProperty(name)
if !exists {
if !obj.extensible {
return reject()
return reject("not exists and not extensible")
}
if newGetSet, isAccessor := descriptor.value.(propertyGetSet); isAccessor {
if newGetSet[0] == &nilGetSetObject {
@@ -347,12 +348,12 @@ func objectDefineOwnProperty(obj *object, name string, descriptor property, thro
configurable := prop.configurable()
if !configurable {
if descriptor.configurable() {
return reject()
return reject("property and descriptor not configurable")
}
// Test that, if enumerable is set on the property descriptor, then it should
// be the same as the existing property
if descriptor.enumerateSet() && descriptor.enumerable() != prop.enumerable() {
return reject()
return reject("property not configurable and enumerable miss match")
}
}
@@ -364,17 +365,17 @@ func objectDefineOwnProperty(obj *object, name string, descriptor property, thro
case isDataDescriptor != descriptor.isDataDescriptor():
// DataDescriptor <=> AccessorDescriptor
if !configurable {
return reject()
return reject("property descriptor not configurable")
}
case isDataDescriptor && descriptor.isDataDescriptor():
// DataDescriptor <=> DataDescriptor
if !configurable {
if !prop.writable() && descriptor.writable() {
return reject()
return reject("property not configurable or writeable and descriptor not writeable")
}
if !prop.writable() {
if descriptor.value != nil && !sameValue(value, descriptor.value.(Value)) {
return reject()
return reject("property not configurable or writeable and descriptor not the same")
}
}
}
@@ -400,7 +401,7 @@ func objectDefineOwnProperty(obj *object, name string, descriptor property, thro
}
if !configurable {
if (presentGet && (getSet[0] != newGetSet[0])) || (presentSet && (getSet[1] != newGetSet[1])) {
return reject()
return reject("access descriptor not configurable")
}
}
descriptor.value = newGetSet

View File

@@ -63,7 +63,7 @@ func TestObject_create(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: Object.create()`, "TypeError")
test(`raise: Object.create()`, "TypeError: Object.Create is nil")
test(`
var abc = Object.create(null)
@@ -121,7 +121,7 @@ func TestObject_isExtensible(t *testing.T) {
test(`raise:
Object.isExtensible();
`, "TypeError")
`, "TypeError: Object.IsExtensible is nil")
// FIXME terst, Why raise?
test(`raise:
@@ -139,7 +139,7 @@ func TestObject_preventExtensions(t *testing.T) {
test(`raise:
Object.preventExtensions()
`, "TypeError")
`, "TypeError: Object.PreventExtensions is nil")
test(`raise:
var abc = { def: true };
@@ -178,7 +178,7 @@ func TestObject_seal(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: Object.seal()`, "TypeError")
test(`raise: Object.seal()`, "TypeError: Object.Seal is nil")
test(`
var abc = {a:1,b:1,c:3};
@@ -211,7 +211,7 @@ func TestObject_isFrozen(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: Object.isFrozen()`, "TypeError")
test(`raise: Object.isFrozen()`, "TypeError: Object.IsFrozen is nil")
test(`Object.isFrozen(Object.preventExtensions({a:1}))`, false)
test(`Object.isFrozen({})`, false)
@@ -235,7 +235,7 @@ func TestObject_freeze(t *testing.T) {
tt(t, func() {
test, _ := test()
test(`raise: Object.freeze()`, "TypeError")
test(`raise: Object.freeze()`, "TypeError: Object.Freeze is nil")
test(`
var abc = {a:1,b:2,c:3};
@@ -395,7 +395,7 @@ func TestObjectGetterSetter(t *testing.T) {
writable: true
}
}).abc;
`, "TypeError")
`, "TypeError: toPropertyDescriptor descriptor writeSet")
test(`raise:
Object.create({}, {
@@ -406,7 +406,7 @@ func TestObjectGetterSetter(t *testing.T) {
writable: false
}
}).abc;
`, "TypeError")
`, "TypeError: toPropertyDescriptor descriptor writeSet")
test(`
Object.create({}, {
@@ -512,7 +512,7 @@ func TestObjectGetterSetter(t *testing.T) {
return 2;
}
});
`, "TypeError")
`, `TypeError: Array.DefineOwnProperty ["function () {\n return 2;\n }" <nil>] is not a value`)
test(`
var abc = {};

View File

@@ -21,7 +21,7 @@ func Test_panic(t *testing.T) {
var abc = [];
Object.defineProperty(abc, "0", { writable: false });
Object.defineProperty(abc, "0", { value: false, writable: false });
`, "TypeError")
`, "TypeError: Array.DefineOwnProperty Object.DefineOwnProperty failed")
// Test that a regular expression can contain \c0410 (CYRILLIC CAPITAL LETTER A)
// without panicking

View File

@@ -117,7 +117,7 @@ func (p property) isEmpty() bool {
func toPropertyDescriptor(rt *runtime, value Value) property {
objectDescriptor := value.object()
if objectDescriptor == nil {
panic(rt.panicTypeError())
panic(rt.panicTypeError("toPropertyDescriptor on nil"))
}
var descriptor property
@@ -153,7 +153,7 @@ func toPropertyDescriptor(rt *runtime, value Value) property {
value := objectDescriptor.get("get")
if value.IsDefined() {
if !value.isCallable() {
panic(rt.panicTypeError())
panic(rt.panicTypeError("toPropertyDescriptor get not callable"))
}
getter = value.object()
getterSetter = true
@@ -167,7 +167,7 @@ func toPropertyDescriptor(rt *runtime, value Value) property {
value := objectDescriptor.get("set")
if value.IsDefined() {
if !value.isCallable() {
panic(rt.panicTypeError())
panic(rt.panicTypeError("toPropertyDescriptor set not callable"))
}
setter = value.object()
getterSetter = true
@@ -179,14 +179,14 @@ func toPropertyDescriptor(rt *runtime, value Value) property {
if getterSetter {
if descriptor.writeSet() {
panic(rt.panicTypeError())
panic(rt.panicTypeError("toPropertyDescriptor descriptor writeSet"))
}
descriptor.value = propertyGetSet{getter, setter}
}
if objectDescriptor.hasProperty("value") {
if getterSetter {
panic(rt.panicTypeError())
panic(rt.panicTypeError("toPropertyDescriptor value getterSetter"))
}
descriptor.value = objectDescriptor.get("value")
}

View File

@@ -147,7 +147,7 @@ func (rt *runtime) tryCatchEvaluate(inner func() Value) (tryValue Value, isExcep
func (rt *runtime) toObject(value Value) *object {
switch value.kind {
case valueEmpty, valueUndefined, valueNull:
panic(rt.panicTypeError())
panic(rt.panicTypeError("toObject unsupported kind %s", value.kind))
case valueBoolean:
return rt.newBoolean(value)
case valueString:
@@ -157,7 +157,7 @@ func (rt *runtime) toObject(value Value) *object {
case valueObject:
return value.object()
default:
panic(rt.panicTypeError())
panic(rt.panicTypeError("toObject unknown kind %s", value.kind))
}
}
@@ -176,19 +176,18 @@ func (rt *runtime) objectCoerce(value Value) (*object, error) {
case valueObject:
return value.object(), nil
default:
panic(rt.panicTypeError())
panic(rt.panicTypeError("objectCoerce unknown kind %s", value.kind))
}
}
func checkObjectCoercible(rt *runtime, value Value) {
isObject, mustCoerce := testObjectCoercible(value)
if !isObject && !mustCoerce {
panic(rt.panicTypeError())
panic(rt.panicTypeError("checkObjectCoercible not object or mustCoerce"))
}
}
// testObjectCoercible
// testObjectCoercible.
func testObjectCoercible(value Value) (isObject, mustCoerce bool) { //nolint: nonamedreturns
switch value.kind {
case valueReference, valueEmpty, valueNull, valueUndefined:
@@ -198,7 +197,7 @@ func testObjectCoercible(value Value) (isObject, mustCoerce bool) { //nolint: no
case valueObject:
return true, false
default:
panic("this should never happen")
panic(fmt.Sprintf("testObjectCoercible unknown kind %s", value.kind))
}
}
@@ -534,7 +533,7 @@ func (rt *runtime) convertCallParameter(v Value, t reflect.Type) (reflect.Value,
r, err := rt.convertCallParameter(rv, t.Out(0))
if err != nil {
panic(rt.panicTypeError(err.Error()))
panic(rt.panicTypeError("convertCallParameter Func: %s", err))
}
return []reflect.Value{r}

View File

@@ -627,11 +627,11 @@ func Test_instanceof(t *testing.T) {
test(`raise:
abc = {} instanceof "abc";
`, "TypeError: Expecting a function in instanceof check, but got: abc")
`, "TypeError: invalid kind String for instanceof (expected object)")
test(`raise:
"xyzzy" instanceof Math;
`, "TypeError")
`, "TypeError: Object.hasInstance not callable")
})
}

View File

@@ -194,7 +194,7 @@ func (s *dclStash) getBinding(name string, throw bool) Value {
}
if !prop.mutable && !prop.readable {
if throw { // strict?
panic(s.rt.panicTypeError())
panic(s.rt.panicTypeError("getBinding property %s not mutable and not readable", name))
}
return Value{}
}

View File

@@ -55,6 +55,13 @@ func arrayDefineOwnProperty(obj *object, name string, descriptor property, throw
if !valid {
panic("Array.length != Value{}")
}
reject := func(reason string) bool {
if throw {
panic(obj.runtime.panicTypeError("Array.DefineOwnProperty %s", reason))
}
return false
}
length := lengthValue.value.(uint32)
if name == propertyLength {
if descriptor.value == nil {
@@ -62,7 +69,7 @@ func arrayDefineOwnProperty(obj *object, name string, descriptor property, throw
}
newLengthValue, isValue := descriptor.value.(Value)
if !isValue {
panic(obj.runtime.panicTypeError())
panic(obj.runtime.panicTypeError("Array.DefineOwnProperty %q is not a value", descriptor.value))
}
newLength := arrayUint32(obj.runtime, newLengthValue)
descriptor.value = uint32Value(newLength)
@@ -70,7 +77,7 @@ func arrayDefineOwnProperty(obj *object, name string, descriptor property, throw
return objectDefineOwnProperty(obj, name, descriptor, throw)
}
if !lengthProperty.writable() {
goto Reject
return reject("property length for not writable")
}
newWritable := true
if descriptor.mode&0o700 == 0 {
@@ -89,7 +96,7 @@ func arrayDefineOwnProperty(obj *object, name string, descriptor property, throw
descriptor.mode &= 0o077
}
objectDefineOwnProperty(obj, name, descriptor, false)
goto Reject
return reject("delete failed")
}
}
if !newWritable {
@@ -98,10 +105,10 @@ func arrayDefineOwnProperty(obj *object, name string, descriptor property, throw
}
} else if index := stringToArrayIndex(name); index >= 0 {
if index >= int64(length) && !lengthProperty.writable() {
goto Reject
return reject("property length not writable")
}
if !objectDefineOwnProperty(obj, strconv.FormatInt(index, 10), descriptor, false) {
goto Reject
return reject("Object.DefineOwnProperty failed")
}
if index >= int64(length) {
lengthProperty.value = uint32Value(uint32(index + 1))
@@ -110,9 +117,4 @@ func arrayDefineOwnProperty(obj *object, name string, descriptor property, throw
}
}
return objectDefineOwnProperty(obj, name, descriptor, throw)
Reject:
if throw {
panic(obj.runtime.panicTypeError())
}
return false
}

View File

@@ -140,8 +140,11 @@ func (o *object) dateValue() dateObject {
}
func dateObjectOf(rt *runtime, date *object) dateObject {
if date == nil || date.class != classDateName {
panic(rt.panicTypeError())
if date == nil {
panic(rt.panicTypeError("Date.ObjectOf is nil"))
}
if date.class != classDateName {
panic(rt.panicTypeError("Date.ObjectOf %q != %q", date.class, classDateName))
}
return date.dateValue()
}

View File

@@ -107,8 +107,9 @@ func (fn bindFunctionObject) construct(argumentList []Value) Value {
case nodeFunctionObject:
argumentList = append(fn.argumentList, argumentList...)
return obj.construct(argumentList)
default:
panic(fn.target.runtime.panicTypeError("construct unknown type %T", obj.value))
}
panic(fn.target.runtime.panicTypeError())
}
// nodeFunctionObject.
@@ -246,14 +247,14 @@ func (o *object) construct(argumentList []Value) Value {
func (o *object) hasInstance(of Value) bool {
if !o.isCall() {
// We should not have a hasInstance method
panic(o.runtime.panicTypeError())
panic(o.runtime.panicTypeError("Object.hasInstance not callable"))
}
if !of.IsObject() {
return false
}
prototype := o.get("prototype")
if !prototype.IsObject() {
panic(o.runtime.panicTypeError())
panic(o.runtime.panicTypeError("Object.hasInstance prototype %q is not an object", prototype))
}
prototypeObject := prototype.object()
@@ -306,7 +307,7 @@ func (f *FunctionCall) thisObject() *object {
func (f *FunctionCall) thisClassObject(class string) *object {
if o := f.thisObject(); o.class != class {
panic(f.runtime.panicTypeError())
panic(f.runtime.panicTypeError("Function.Class %s != %s", o.class, class))
}
return f.thisObj
}

View File

@@ -69,7 +69,7 @@ func (o goStructObject) setValue(rt *runtime, name string, value Value) bool {
fieldValue := o.getValue(name)
converted, err := rt.convertCallParameter(value, fieldValue.Type())
if err != nil {
panic(rt.panicTypeError(err.Error()))
panic(rt.panicTypeError("Object.setValue convertCallParameter: %s", err))
}
fieldValue.Set(converted)

View File

@@ -118,10 +118,7 @@ func (v Value) call(rt *runtime, this Value, argumentList ...interface{}) Value
if function, ok := v.value.(*object); ok {
return function.call(this, function.runtime.toValueArray(argumentList...), false, nativeFrame)
}
if rt == nil {
panic("FIXME TypeError")
}
panic(rt.panicTypeError())
panic(rt.panicTypeError("call %q is not an object", v.value))
}
func (v Value) constructSafe(rt *runtime, this Value, argumentList ...interface{}) (Value, error) {
@@ -136,10 +133,7 @@ func (v Value) construct(rt *runtime, this Value, argumentList ...interface{}) V
if fn, ok := v.value.(*object); ok {
return fn.construct(fn.runtime.toValueArray(argumentList...))
}
if rt == nil {
panic("FIXME TypeError")
}
panic(rt.panicTypeError())
panic(rt.panicTypeError("construct %q is not an object", v.value))
}
// IsPrimitive will return true if value is a primitive (any kind of primitive).

31
value_kind.gen.go Normal file
View File

@@ -0,0 +1,31 @@
// Code generated by "stringer -type=valueKind -trimprefix=value -output=value_kind.gen.go"; DO NOT EDIT.
package otto
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[valueUndefined-0]
_ = x[valueNull-1]
_ = x[valueNumber-2]
_ = x[valueString-3]
_ = x[valueBoolean-4]
_ = x[valueObject-5]
_ = x[valueEmpty-6]
_ = x[valueResult-7]
_ = x[valueReference-8]
}
const _valueKind_name = "UndefinedNullNumberStringBooleanObjectEmptyResultReference"
var _valueKind_index = [...]uint8{0, 9, 13, 19, 25, 32, 38, 43, 49, 58}
func (i valueKind) String() string {
if i < 0 || i >= valueKind(len(_valueKind_index)-1) {
return "valueKind(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _valueKind_name[_valueKind_index[i]:_valueKind_index[i+1]]
}