Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
manifold.js.JavascriptClass Maven / Gradle / Ivy
/*
* Copyright (c) 2020 - Manifold Systems LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package manifold.js;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import manifold.api.fs.IFile;
import manifold.api.fs.IFileFragment;
import manifold.api.gen.AbstractSrcMethod;
import manifold.api.gen.SrcAnnotationExpression;
import manifold.api.gen.SrcClass;
import manifold.api.gen.SrcConstructor;
import manifold.api.gen.SrcField;
import manifold.api.gen.SrcMethod;
import manifold.api.gen.SrcParameter;
import manifold.api.type.ResourceFileTypeManifold;
import manifold.rt.api.SourcePosition;
import manifold.js.rt.JsRuntime;
import manifold.js.rt.parser.tree.ClassFunctionNode;
import manifold.js.rt.parser.tree.ClassNode;
import manifold.js.rt.parser.tree.ConstructorNode;
import manifold.js.rt.parser.tree.ParameterNode;
import manifold.js.rt.parser.tree.ProgramNode;
import manifold.js.rt.parser.tree.PropertyNode;
import manifold.rt.api.util.ManClassUtil;
import manifold.rt.api.util.ManEscapeUtil;
import org.mozilla.javascript.ScriptableObject;
public class JavascriptClass
{
static SrcClass genClass( String fqn, JavascriptModel model, ProgramNode programNode, IFile file )
{
ClassNode classNode = programNode.getFirstChild( ClassNode.class );
SrcClass clazz = new SrcClass( fqn, SrcClass.Kind.Class )
.imports( JsRuntime.class )
.imports( SourcePosition.class );
clazz.addAnnotation(
new SrcAnnotationExpression( SourcePosition.class )
.addArgument( "url", String.class, programNode.getUrl().toString() )
.addArgument( "feature", String.class, ManClassUtil.getShortClassName( fqn ) )
.addArgument( "offset", int.class, absoluteOffset( classNode.getStart().getOffset(), file ) )
.addArgument( "length", int.class, classNode.getEnd().getOffset() - classNode.getStart().getOffset() ) );
String superClass = classNode.getSuperClass();
if( superClass != null )
{
clazz.superClass( superClass );
}
clazz.addField( new SrcField( "SCOPE", ScriptableObject.class )
.modifiers( Modifier.PRIVATE | Modifier.STATIC | Modifier.VOLATILE )
.initializer( "null" ) );
clazz.addField( new SrcField( "UID", long.class )
.modifiers( Modifier.PRIVATE | Modifier.STATIC | Modifier.VOLATILE )
.initializer( "0L" ) );
clazz.addField( new SrcField( "_context", ScriptableObject.class ) );
addConstructor( clazz, classNode, file );
addUtilityMethods( clazz, model, classNode, fqn );
addMethods( fqn, clazz, classNode, file );
addProperties( fqn, clazz, classNode, file );
return clazz;
}
private static void addConstructor( SrcClass clazz, ClassNode classNode, IFile file )
{
ConstructorNode constructor = classNode.getFirstChild( ConstructorNode.class );
SrcConstructor ctor = new SrcConstructor()
.name( classNode.getName() )
.modifiers( Modifier.PUBLIC );
List srcParameters;
if( constructor != null )
{
srcParameters = JavascriptProgram.makeSrcParameters( constructor, ctor );
ctor.addAnnotation(
new SrcAnnotationExpression( SourcePosition.class )
.addArgument( "url", String.class, classNode.getProgramNode().getUrl().toString() )
.addArgument( "feature", String.class, "constructor" )
.addArgument( "offset", int.class, absoluteOffset( constructor.getStart().getOffset(), file ) )
.addArgument( "length", int.class, constructor.getEnd().getOffset() - constructor.getStart().getOffset() ) );
}
else
{
srcParameters = Collections.emptyList();
}
//impl
ctor.body( "_context = " + JsRuntime.class.getSimpleName() +
".initInstance(getScope(), \"" + classNode.getName() + "\"" + JavascriptProgram.generateArgList( srcParameters ) + ");" );
clazz.addConstructor( ctor );
}
private static ThreadLocal uid = new ThreadLocal<>();
private static void addUtilityMethods( SrcClass clazz, JavascriptModel model, ClassNode classNode, String fqn )
{
long uid = incUid();
IFile file = JavascriptProgram.loadSrcForName( model, fqn, JavascriptTypeManifold.JS );
String url;
try
{
url = file.toURI().toURL().toString();
}
catch( MalformedURLException e )
{
throw new RuntimeException( e );
}
String content = ResourceFileTypeManifold.getContent( file );
content = ManEscapeUtil.escapeForJavaStringLiteral( content );
SrcMethod m = new SrcMethod()
.name( "getScope" )
.modifiers( Modifier.PRIVATE | Modifier.STATIC )
.returns( ScriptableObject.class )
.body( "if(" + uid + "L != UID) {\n" +
" synchronized(" + classNode.getName() + ".class) {\n" +
" if(" + uid + "L != UID) {\n" +
" UID = " + uid + "L;\n" +
" SCOPE = " + JsRuntime.class.getSimpleName() + ".init(\"" + fqn + "\", \"" + content + "\", \"" + url + "\");\n" +
" }\n" +
" }\n" +
" }\n" +
" return SCOPE;" );
clazz.addMethod( m );
}
private static long incUid()
{
Long id = uid.get();
if( id == null )
{
uid.set( id = 1L );
}
uid.set( ++id );
return id;
}
private static void addMethods( String fqn, SrcClass clazz, ClassNode classNode, IFile file )
{
for( ClassFunctionNode node: classNode.getChildren( ClassFunctionNode.class ) )
{
AbstractSrcMethod srcMethod = new SrcMethod()
.name( node.getName() )
.modifiers( Modifier.PUBLIC | (node.isStatic() ? Modifier.STATIC : 0) )
.returns( node.getReturnType() );
srcMethod.addAnnotation(
new SrcAnnotationExpression( SourcePosition.class )
.addArgument( "url", String.class, classNode.getProgramNode().getUrl().toString() )
.addArgument( "feature", String.class, node.getName() )
.addArgument( "offset", int.class, absoluteOffset( node.getStart().getOffset(), file ) )
.addArgument( "length", int.class, node.getEnd().getOffset() - node.getStart().getOffset() ) );
// params
ParameterNode parameters = node.getFirstChild( ParameterNode.class );
for( SrcParameter srcParameter: toParamList( parameters ) )
{
srcMethod.addParam( srcParameter );
}
//impl
if( node.isStatic() )
{
srcMethod.body( "return " + JsRuntime.class.getSimpleName() + ".invokeStatic(getScope(), \"" +
ManClassUtil.getShortClassName( fqn ) + "\", \"" + node.getName() + "\"" +
JavascriptProgram.generateArgList( toParamList( parameters ) ) + ");" );
}
else
{
srcMethod.body( "return " + JsRuntime.class.getSimpleName() + ".invoke(_context, \"" + node.getName() + "\"" +
JavascriptProgram.generateArgList( toParamList( parameters ) ) + ");" );
}
clazz.addMethod( srcMethod );
}
}
public static List toParamList( ParameterNode parameters )
{
ArrayList params = parameters.getParams();
List parameterInfoBuilders = new ArrayList<>( params.size() );
for( int i = 0; i < params.size(); i++ )
{
parameterInfoBuilders.add( new SrcParameter( params.get( i ), parameters.getTypes().get( i ) ) );
}
return parameterInfoBuilders;
}
private static void addProperties( String fqn, SrcClass clazz, ClassNode classNode, IFile file )
{
for( PropertyNode node: classNode.getChildren( PropertyNode.class ) )
{
final String name = node.getName();
final String capitalizedName = name.substring( 0, 1 ).toUpperCase() + name.substring( 1 );
if( node.isSetter() )
{
AbstractSrcMethod setter = new SrcMethod()
.name( "set" + capitalizedName )
.modifiers( Modifier.PUBLIC | (node.isStatic() ? Modifier.STATIC : 0) )
.addParam( "val", Object.class )
.returns( "void" );
setter.addAnnotation(
new SrcAnnotationExpression( SourcePosition.class )
.addArgument( "url", String.class, classNode.getProgramNode().getUrl().toString() )
.addArgument( "feature", String.class, node.getName() )
.addArgument( "offset", int.class, absoluteOffset( node.getStart().getOffset(), file ) )
.addArgument( "length", int.class, node.getEnd().getOffset() - node.getStart().getOffset() ) );
if( node.isStatic() )
{
setter.body( JsRuntime.class.getSimpleName() + ".setStaticProp(getScope(), \"" +
ManClassUtil.getShortClassName( fqn ) + "\", \"" + name + "\", val);" );
}
else
{
setter.body( JsRuntime.class.getSimpleName() + ".setProp(_context, \"" + name + "\", val);" );
}
clazz.addMethod( setter );
}
else
{
AbstractSrcMethod getter = new SrcMethod()
.name( "get" + capitalizedName )
.modifiers( Modifier.PUBLIC | (node.isStatic() ? Modifier.STATIC : 0) )
.returns( node.getReturnType() );
getter.addAnnotation(
new SrcAnnotationExpression( SourcePosition.class )
.addArgument( "url", String.class, classNode.getProgramNode().getUrl().toString() )
.addArgument( "feature", String.class, node.getName() )
.addArgument( "offset", int.class, node.getStart().getOffset() )
.addArgument( "length", int.class, node.getEnd().getOffset() - node.getStart().getOffset() ) );
//impl
if( node.isStatic() )
{
getter.body( "return " + JsRuntime.class.getSimpleName() + ".getStaticProp(getScope(), \"" +
ManClassUtil.getShortClassName( fqn ) + "\", \"" + name + "\");" );
}
else
{
getter.body( "return " + JsRuntime.class.getSimpleName() + ".getProp(_context, \"" + name + "\");" );
}
clazz.addMethod( getter );
}
}
}
private static Object absoluteOffset( int offset, IFile file )
{
if( file instanceof IFileFragment )
{
offset += ((IFileFragment)file).getOffset();
}
return offset;
}
}