avm2: Make primitive coercions way cheaper

This commit is contained in:
Adrian Wielgosik 2024-08-10 13:53:31 +02:00 committed by TÖRÖK Attila
parent f202088bd8
commit 0097aea381
10 changed files with 190 additions and 19 deletions

View File

@ -656,21 +656,14 @@ impl<'gc> Value<'gc> {
match self {
Value::Object(Object::PrimitiveObject(o)) => o.value_of(activation.context.gc_context),
Value::Object(o) if hint == Hint::String => {
let mut prim = *self;
let object = *o;
if let Value::Object(_) = object.get_public_property("toString", activation)? {
prim = object.call_public_property("toString", &[], activation)?;
}
let prim = object.call_public_property("toString", &[], activation)?;
if prim.is_primitive() {
return Ok(prim);
}
if let Value::Object(_) = object.get_public_property("valueOf", activation)? {
prim = object.call_public_property("valueOf", &[], activation)?;
}
let prim = object.call_public_property("valueOf", &[], activation)?;
if prim.is_primitive() {
return Ok(prim);
}
@ -685,21 +678,14 @@ impl<'gc> Value<'gc> {
)?))
}
Value::Object(o) if hint == Hint::Number => {
let mut prim = *self;
let object = *o;
if let Value::Object(_) = object.get_public_property("valueOf", activation)? {
prim = object.call_public_property("valueOf", &[], activation)?;
}
let prim = object.call_public_property("valueOf", &[], activation)?;
if prim.is_primitive() {
return Ok(prim);
}
if let Value::Object(_) = object.get_public_property("toString", activation)? {
prim = object.call_public_property("toString", &[], activation)?;
}
let prim = object.call_public_property("toString", &[], activation)?;
if prim.is_primitive() {
return Ok(prim);
}

View File

@ -0,0 +1,58 @@
// compiled with mxmlc
import flash.utils.getQualifiedClassName;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
package {
import flash.display.MovieClip;
public class Test extends MovieClip {
}
}
class MyProxy extends Proxy {
public var returnValue;
public function MyProxy(val) { this.returnValue = val; }
flash_proxy override function callProperty(name:*, ...rest):* {
trace("call " + name);
return this.returnValue;
}
flash_proxy override function getProperty(name:*):* {
trace("get " + name);
return undefined;
}
flash_proxy override function hasProperty(name:*):Boolean {
trace("has " + name);
return false;
}
}
function test(obj){
trace("----------- " + obj.returnValue);
var s: String;
var i: int;
try {
s = obj;
trace(getQualifiedClassName(s));
trace(s);
} catch (e) { trace("Error " + e.errorID); }
try {
i = obj;
trace(getQualifiedClassName(i));
trace(i);
} catch (e) { trace("Error " + e.errorID); }
}
function main(){
test(new MyProxy("1234"));
test(new MyProxy(undefined));
test(new MyProxy(new Object()));
trace("--- string null")
var s: String = null;
test(new MyProxy(s));
// coerce_to_primitive_side_effects_with_nulls is the same test, with these uncommented
// trace("--- normal null")
// test(new MyProxy(null));
}
main();

View File

@ -0,0 +1,29 @@
----------- 1234
call toString
String
1234
call valueOf
int
1234
----------- undefined
call toString
String
undefined
call valueOf
int
0
----------- [object Object]
call toString
call valueOf
Error 1050
call valueOf
call toString
Error 1050
--- string null
----------- null
call toString
String
null
call valueOf
int
0

Binary file not shown.

View File

@ -0,0 +1 @@
num_frames = 1

View File

@ -0,0 +1,57 @@
// compiled with mxmlc
import flash.utils.getQualifiedClassName;
import flash.utils.Proxy;
import flash.utils.flash_proxy;
package {
import flash.display.MovieClip;
public class Test extends MovieClip {
}
}
class MyProxy extends Proxy {
public var returnValue;
public function MyProxy(val) { this.returnValue = val; }
flash_proxy override function callProperty(name:*, ...rest):* {
trace("call " + name);
return this.returnValue;
}
flash_proxy override function getProperty(name:*):* {
trace("get " + name);
return undefined;
}
flash_proxy override function hasProperty(name:*):Boolean {
trace("has " + name);
return false;
}
}
function test(obj){
trace("----------- " + obj.returnValue);
var s: String;
var i: int;
try {
s = obj;
trace(getQualifiedClassName(s));
trace(s);
} catch (e) { trace("Error " + e.errorID); }
try {
i = obj;
trace(getQualifiedClassName(i));
trace(i);
} catch (e) { trace("Error " + e.errorID); }
}
function main(){
test(new MyProxy("1234"));
test(new MyProxy(undefined));
test(new MyProxy(new Object()));
trace("--- string null")
var s: String = null;
test(new MyProxy(s));
trace("--- normal null")
test(new MyProxy(null));
}
main();

View File

@ -0,0 +1,37 @@
----------- 1234
call toString
String
1234
call valueOf
int
1234
----------- undefined
call toString
String
undefined
call valueOf
int
0
----------- [object Object]
call toString
call valueOf
Error 1050
call valueOf
call toString
Error 1050
--- string null
----------- null
call toString
String
null
call valueOf
int
0
--- normal null
----------- null
call toString
call valueOf
Error 1050
call valueOf
call toString
Error 1050

View File

@ -0,0 +1,4 @@
num_frames = 1
known_failure = true
# this observes the subtle difference between string-null and object-null
# the toString logic in AVM2 uses an "is Object" check via discriminant check, which only object-null passes.

View File

@ -1,2 +1 @@
num_ticks = 1
known_failure = true