Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for lambda-based native functions #865

Merged
merged 3 commits into from
Apr 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
383 changes: 383 additions & 0 deletions benchmarks/org/mozilla/javascript/benchmarks/BuiltinBenchmark.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,383 @@
package org.mozilla.javascript.benchmarks;

import java.lang.reflect.InvocationTargetException;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.IdFunctionObject;
import org.mozilla.javascript.IdScriptableObject;
import org.mozilla.javascript.LambdaConstructor;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.annotations.*;
import org.openjdk.jmh.annotations.*;

public class BuiltinBenchmark {

@State(Scope.Thread)
public static class AbstractClassState {

public void init()
throws IllegalAccessException, InvocationTargetException, InstantiationException {
cx = Context.enter();
cx.setOptimizationLevel(9);
cx.setLanguageVersion(Context.VERSION_ES6);

scope = cx.initStandardObjects();
ScriptableObject.defineClass(scope, AnnotatedClass.class);
IdClass.init(scope);
DumbLambdaClass.init(scope);
}

void compileScript(String testClassName) {
testScript =
cx.compileString(
"o = new "
+ testClassName
+ "();\n"
+ "o.setValue(99);\n"
+ "for (var i = 0; i < 1000; i++) {\n"
+ "if (o.getValue() !== 99) { throw 'Not working!'; }\n"
+ "}\n"
+ "o.getValue();",
"test.js",
1,
null);
}

@TearDown(Level.Trial)
public void close() {
Context.exit();
}

Context cx;
Scriptable scope;
Script testScript;
}

@State(Scope.Thread)
public static class AnnotatedClassState extends AbstractClassState {

@Setup(Level.Trial)
public void init()
throws IllegalAccessException, InvocationTargetException, InstantiationException {
super.init();
compileScript("AnnotatedClass");
}
}

@Benchmark
public Object annotatedClassMethods(AnnotatedClassState state) {
return state.testScript.exec(state.cx, state.scope);
}

public static class AnnotatedClass extends ScriptableObject {

@Override
public String getClassName() {
return "AnnotatedClass";
}

@JSFunction
public void one() {}

@JSFunction
public void two() {}

@JSFunction
public void three() {}

@JSFunction
public void four() {}

@JSFunction
public void five() {}

@JSFunction
public void six() {}

@JSFunction
public void seven() {}

@JSFunction
public void eight() {}

@JSFunction
public void nine() {}

@JSFunction
public void setValue(int value) {
this.value = value;
}

@JSFunction
public int getValue() {
return value;
}

private int value;
}

@State(Scope.Thread)
public static class IdClassState extends AbstractClassState {

@Setup(Level.Trial)
public void init()
throws IllegalAccessException, InvocationTargetException, InstantiationException {
super.init();
compileScript("IdClass");
}
}

@Benchmark
public Object idClassMethods(IdClassState state) {
return state.testScript.exec(state.cx, state.scope);
}

public static class IdClass extends IdScriptableObject {

private static final String TAG = "IdClass";

public static void init(Scriptable scope) {
IdClass idc = new IdClass();
idc.exportAsJSClass(MAX_ID, scope, false);
}

@Override
public String getClassName() {
return "IdClass";
}

@Override
protected void initPrototypeId(int id) {
String s, fnName = null;
int arity;
switch (id) {
case Id_one:
arity = 0;
s = "one";
break;
case Id_two:
arity = 0;
s = "two";
break;
case Id_three:
arity = 0;
s = "three";
break;
case Id_four:
arity = 0;
s = "four";
break;
case Id_five:
arity = 0;
s = "five";
break;
case Id_six:
arity = 0;
s = "six";
break;
case Id_seven:
arity = 0;
s = "seven";
break;
case Id_eight:
arity = 0;
s = "eight";
break;
case Id_nine:
arity = 0;
s = "nine";
break;
case Id_setValue:
arity = 1;
s = "setValue";
break;
case Id_getValue:
arity = 0;
s = "getValue";
break;
case Id_constructor:
arity = 0;
s = "constructor";
break;
default:
throw new IllegalArgumentException(String.valueOf(id));
}

initPrototypeMethod(TAG, id, s, fnName, arity);
}

@Override
public Object execIdCall(
IdFunctionObject f,
Context cx,
Scriptable scope,
Scriptable thisObj,
Object[] args) {
if (!f.hasTag(TAG)) {
return super.execIdCall(f, cx, scope, thisObj, args);
}
int id = f.methodId();
IdClass self;
switch (id) {
case Id_constructor:
return new IdClass();
case Id_setValue:
self = (IdClass) thisObj;
if (args.length < 1) {
throw ScriptRuntime.throwError(cx, scope, "not enough args");
}
self.value = ScriptRuntime.toInt32(args[0]);
break;
case Id_getValue:
self = (IdClass) thisObj;
return self.value;
default:
throw new IllegalArgumentException(
"Array.prototype has no method: " + f.getFunctionName());
}
return Undefined.instance;
}

// #string_id_map#

@Override
protected int findPrototypeId(String s) {
int id;
// #generated# Last update: 2021-04-13 16:17:26 PDT
switch (s) {
case "one":
id = Id_one;
break;
case "two":
id = Id_two;
break;
case "three":
id = Id_three;
break;
case "four":
id = Id_four;
break;
case "five":
id = Id_five;
break;
case "six":
id = Id_six;
break;
case "seven":
id = Id_seven;
break;
case "eight":
id = Id_eight;
break;
case "nine":
id = Id_nine;
break;
case "getValue":
id = Id_getValue;
break;
case "setValue":
id = Id_setValue;
break;
case "constructor":
id = Id_constructor;
break;
default:
id = 0;
break;
}
// #/generated#
return id;
}

private static final int Id_one = 1,
Id_two = 2,
Id_three = 3,
Id_four = 4,
Id_five = 5,
Id_six = 6,
Id_seven = 7,
Id_eight = 8,
Id_nine = 9,
Id_getValue = 10,
Id_setValue = 11,
Id_constructor = 12,
MAX_ID = Id_constructor;

// #/string_id_map#

private int value;
}

@State(Scope.Thread)
public static class DumbLambdaState extends AbstractClassState {

@Setup(Level.Trial)
public void init()
throws IllegalAccessException, InvocationTargetException, InstantiationException {
super.init();
compileScript("DumbLambdaClass");
}
}

@Benchmark
public Object dumbLambdaClassMethods(DumbLambdaState state) {
return state.testScript.exec(state.cx, state.scope);
}

private static class DumbLambdaClass extends ScriptableObject {

private static Object noop(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
return Undefined.instance;
}

private static Object setValue(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
if (args.length < 1) {
throw ScriptRuntime.throwError(cx, scope, "Not enough args");
}
DumbLambdaClass self =
LambdaConstructor.convertThisObject(thisObj, DumbLambdaClass.class);
self.value = ScriptRuntime.toInt32(args[0]);
return Undefined.instance;
}

private static Object getValue(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
DumbLambdaClass self =
LambdaConstructor.convertThisObject(thisObj, DumbLambdaClass.class);
return self.value;
}

public static void init(Scriptable scope) {
LambdaConstructor cons =
new LambdaConstructor(
scope,
"DumbLambdaClass",
0,
(Context cx, Scriptable s, Object[] args) -> new DumbLambdaClass());
cons.definePrototypeMethod(scope, "one", 0, DumbLambdaClass::noop);
cons.definePrototypeMethod(scope, "two", 0, DumbLambdaClass::noop);
cons.definePrototypeMethod(scope, "three", 0, DumbLambdaClass::noop);
cons.definePrototypeMethod(scope, "four", 0, DumbLambdaClass::noop);
cons.definePrototypeMethod(scope, "five", 0, DumbLambdaClass::noop);
cons.definePrototypeMethod(scope, "six", 0, DumbLambdaClass::noop);
cons.definePrototypeMethod(scope, "seven", 0, DumbLambdaClass::noop);
cons.definePrototypeMethod(scope, "eight", 0, DumbLambdaClass::noop);
cons.definePrototypeMethod(scope, "nine", 0, DumbLambdaClass::noop);
cons.definePrototypeMethod(scope, "setValue", 1, DumbLambdaClass::setValue);
cons.definePrototypeMethod(scope, "getValue", 1, DumbLambdaClass::getValue);
ScriptableObject.putProperty(scope, "DumbLambdaClass", cons);
}

@Override
public String getClassName() {
return "DumbLambdaClass";
}

private int value;
}
}
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ task testBenchmark() {}
testBenchmark.dependsOn sunSpiderBenchmark
testBenchmark.dependsOn v8Benchmark

task microBenchmarks(type: JavaExec, description: 'JMH benchmarks') {
task builtinBenchmarks(type: JavaExec, description: 'JMH benchmarks') {
classpath = sourceSets.jmh.runtimeClasspath
main = 'org.openjdk.jmh.Main'
args '-f', '1', '-bm', 'avgt', '-tu', 'ns', 'ObjectCall'
args '-f', '1', '-bm', 'avgt', '-tu', 'ns', 'Builtin'
}

task jmhHelp(type: JavaExec, description: 'JMH benchmarks') {
Expand Down
Loading