All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.javascript.jscomp.serialization.ScriptNodeDeserializer Maven / Gradle / Ivy

/*
 * Copyright 2021 The Closure Compiler Authors.
 *
 * 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 com.google.javascript.jscomp.serialization;

import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.javascript.jscomp.SourceFile;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.ExtensionRegistry;
import java.io.IOException;
import java.math.BigInteger;
import org.jspecify.nullness.Nullable;

/**
 * Class that deserializes an AstNode-tree representing a SCRIPT into a Node-tree.
 *
 * 

This process depends on other information from the TypedAST format, but the output it limited * to only a single SCRIPT. The other deserialized content must be provided beforehand. */ @GwtIncompatible("protobuf.lite") final class ScriptNodeDeserializer { private final SourceFile sourceFile; private final ByteString scriptBytes; private final Optional colorPoolShard; private final StringPool stringPool; private final ImmutableList filePool; ScriptNodeDeserializer( LazyAst ast, StringPool stringPool, Optional colorPoolShard, ImmutableList filePool) { this.scriptBytes = ast.getScript(); this.sourceFile = filePool.get(ast.getSourceFile() - 1); this.colorPoolShard = colorPoolShard; this.stringPool = stringPool; this.filePool = filePool; } public Node deserializeNew() { return new Runner().run(); } private final class Runner { private FeatureSet scriptFeatures = FeatureSet.BARE_MINIMUM; private int previousLine = 0; private int previousColumn = 0; Node run() { try { CodedInputStream astStream = this.owner().scriptBytes.newCodedInput(); astStream.setRecursionLimit(Integer.MAX_VALUE); // The real limit is stack space. Node scriptNode = this.visit( AstNode.parseFrom(astStream, ExtensionRegistry.getEmptyRegistry()), null, this.owner().createSourceInfoTemplate(this.owner().sourceFile)); scriptNode.putProp(Node.FEATURE_SET, this.scriptFeatures); return scriptNode; } catch (IOException ex) { throw new MalformedTypedAstException(this.owner().sourceFile, ex); } } private ScriptNodeDeserializer owner() { return ScriptNodeDeserializer.this; } private Node visit(AstNode astNode, @Nullable Node parent, @Nullable Node sourceFileTemplate) { if (sourceFileTemplate == null || astNode.getSourceFile() != 0) { // 0 == 'not set' sourceFileTemplate = this.owner() .createSourceInfoTemplate(this.owner().filePool.get(astNode.getSourceFile() - 1)); } int currentLine = this.previousLine + astNode.getRelativeLine(); int currentColumn = this.previousColumn + astNode.getRelativeColumn(); Node n = this.owner().deserializeSingleNode(astNode); n.setStaticSourceFileFrom(sourceFileTemplate); if (astNode.hasType() && this.owner().colorPoolShard.isPresent()) { n.setColor(this.owner().colorPoolShard.get().getColor(astNode.getType())); } long properties = astNode.getBooleanProperties(); if (properties > 0) { n.deserializeProperties(filterOutCastProp(astNode.getBooleanProperties())); } n.setJSDocInfo(JSDocSerializer.deserializeJsdoc(astNode.getJsdoc(), stringPool)); n.setLinenoCharno(currentLine, currentColumn); this.previousLine = currentLine; this.previousColumn = currentColumn; int children = astNode.getChildCount(); for (int i = 0; i < children; i++) { AstNode child = astNode.getChild(i); Node deserializedChild = this.visit(child, n, sourceFileTemplate); n.addChildToBack(deserializedChild); // record script features here instead of while visiting child because some features are // context-dependent, and we need to know the parent and/or grandparent. this.recordScriptFeatures(parent, n, deserializedChild); this.owner().setOriginalNameIfPresent(child, deserializedChild); } return n; } private void recordScriptFeatures(Node grandparent, Node parent, Node node) { if (parent.isClass() && !node.isEmpty() && node.isSecondChildOf(parent)) { this.addScriptFeature(Feature.CLASS_EXTENDS); } switch (node.getToken()) { case FUNCTION: if (node.isAsyncGeneratorFunction()) { this.addScriptFeature(Feature.ASYNC_GENERATORS); } if (node.isArrowFunction()) { this.addScriptFeature(Feature.ARROW_FUNCTIONS); } if (node.isAsyncFunction()) { this.addScriptFeature(Feature.ASYNC_FUNCTIONS); } if (node.isGeneratorFunction()) { this.addScriptFeature(Feature.GENERATORS); } if (parent.isBlock() && !grandparent.isFunction()) { this.scriptFeatures = this.scriptFeatures.with(Feature.BLOCK_SCOPED_FUNCTION_DECLARATION); } return; case PARAM_LIST: case CALL: case NEW: if (node.hasTrailingComma()) { this.addScriptFeature(Feature.TRAILING_COMMA_IN_PARAM_LIST); } return; case STRING_KEY: if (node.isShorthandProperty()) { this.addScriptFeature(Feature.EXTENDED_OBJECT_LITERALS); } return; case DEFAULT_VALUE: if (parent.isParamList()) { this.addScriptFeature(Feature.DEFAULT_PARAMETERS); } return; case GETTER_DEF: this.addScriptFeature(Feature.GETTER); if (parent.isClassMembers()) { this.addScriptFeature(Feature.CLASS_GETTER_SETTER); } return; case SETTER_DEF: this.addScriptFeature(Feature.SETTER); if (parent.isClassMembers()) { this.addScriptFeature(Feature.CLASS_GETTER_SETTER); } return; case BLOCK: if (parent.isClassMembers()) { this.addScriptFeature(Feature.CLASS_STATIC_BLOCK); } return; case EMPTY: if (parent.isCatch()) { this.addScriptFeature(Feature.OPTIONAL_CATCH_BINDING); } return; case ITER_REST: this.addScriptFeature(Feature.ARRAY_PATTERN_REST); if (parent.isParamList()) { this.addScriptFeature(Feature.REST_PARAMETERS); } return; case ITER_SPREAD: this.addScriptFeature(Feature.SPREAD_EXPRESSIONS); return; case OBJECT_REST: this.addScriptFeature(Feature.OBJECT_PATTERN_REST); return; case OBJECT_SPREAD: this.addScriptFeature(Feature.OBJECT_LITERALS_WITH_SPREAD); return; case BIGINT: this.addScriptFeature(Feature.BIGINT); return; case EXPONENT: case ASSIGN_EXPONENT: this.addScriptFeature(Feature.EXPONENT_OP); return; case TAGGED_TEMPLATELIT: case TEMPLATELIT: this.addScriptFeature(Feature.TEMPLATE_LITERALS); return; case NEW_TARGET: this.addScriptFeature(Feature.NEW_TARGET); return; case COMPUTED_PROP: this.addScriptFeature(Feature.COMPUTED_PROPERTIES); return; case OPTCHAIN_GETPROP: case OPTCHAIN_CALL: case OPTCHAIN_GETELEM: this.addScriptFeature(Feature.OPTIONAL_CHAINING); return; case COALESCE: this.addScriptFeature(Feature.NULL_COALESCE_OP); return; case DYNAMIC_IMPORT: this.addScriptFeature(Feature.DYNAMIC_IMPORT); return; case ASSIGN_OR: case ASSIGN_AND: this.addScriptFeature(Feature.LOGICAL_ASSIGNMENT); return; case ASSIGN_COALESCE: this.addScriptFeature(Feature.NULL_COALESCE_OP); this.addScriptFeature(Feature.LOGICAL_ASSIGNMENT); return; case FOR_OF: this.addScriptFeature(Feature.FOR_OF); return; case FOR_AWAIT_OF: this.addScriptFeature(Feature.FOR_AWAIT_OF); return; case IMPORT: case EXPORT: this.addScriptFeature(Feature.MODULES); return; case CONST: this.addScriptFeature(Feature.CONST_DECLARATIONS); return; case LET: this.addScriptFeature(Feature.LET_DECLARATIONS); return; case CLASS: this.addScriptFeature(Feature.CLASSES); return; case CLASS_MEMBERS: case MEMBER_FUNCTION_DEF: this.addScriptFeature(Feature.MEMBER_DECLARATIONS); return; case MEMBER_FIELD_DEF: case COMPUTED_FIELD_DEF: this.addScriptFeature(Feature.PUBLIC_CLASS_FIELDS); return; case SUPER: this.addScriptFeature(Feature.SUPER); return; case ARRAY_PATTERN: this.addScriptFeature(Feature.ARRAY_DESTRUCTURING); return; case OBJECT_PATTERN: this.addScriptFeature(Feature.OBJECT_DESTRUCTURING); return; default: return; } } private void addScriptFeature(Feature x) { this.scriptFeatures = this.scriptFeatures.with(x); } } /** * Create a template node to use as a source of common attributes. * *

This allows the prop structure to be shared among all the node from this source file. This * reduces the cost of these properties to O(nodes) to O(files). */ private Node createSourceInfoTemplate(SourceFile file) { // The Node type choice is arbitrary. Node sourceInfoTemplate = new Node(Token.SCRIPT); sourceInfoTemplate.setStaticSourceFile(file); return sourceInfoTemplate; } private void setOriginalNameIfPresent(AstNode astNode, Node n) { if (astNode.getOriginalNamePointer() != 0) { n.setOriginalName(this.stringPool.get(astNode.getOriginalNamePointer())); } } private String getString(AstNode n) { return this.stringPool.get(n.getStringValuePointer()); } private Node deserializeSingleNode(AstNode n) { switch (n.getKind()) { case SOURCE_FILE: return new Node(Token.SCRIPT); case NUMBER_LITERAL: return IR.number(n.getDoubleValue()); case STRING_LITERAL: return IR.string(getString(n)); case IDENTIFIER: return IR.name(getString(n)); case FALSE: return new Node(Token.FALSE); case TRUE: return new Node(Token.TRUE); case NULL: return new Node(Token.NULL); case THIS: return new Node(Token.THIS); case VOID: return new Node(Token.VOID); case BIGINT_LITERAL: return IR.bigint(new BigInteger(getString(n))); case REGEX_LITERAL: return new Node(Token.REGEXP); case ARRAY_LITERAL: return new Node(Token.ARRAYLIT); case OBJECT_LITERAL: return new Node(Token.OBJECTLIT); case ASSIGNMENT: return new Node(Token.ASSIGN); case CALL: return new Node(Token.CALL); case NEW: return new Node(Token.NEW); case PROPERTY_ACCESS: return Node.newString(Token.GETPROP, getString(n)); case ELEMENT_ACCESS: return new Node(Token.GETELEM); case COMMA: return new Node(Token.COMMA); case BOOLEAN_OR: return new Node(Token.OR); case BOOLEAN_AND: return new Node(Token.AND); case HOOK: return new Node(Token.HOOK); case EQUAL: return new Node(Token.EQ); case NOT_EQUAL: return new Node(Token.NE); case LESS_THAN: return new Node(Token.LT); case LESS_THAN_EQUAL: return new Node(Token.LE); case GREATER_THAN: return new Node(Token.GT); case GREATER_THAN_EQUAL: return new Node(Token.GE); case TRIPLE_EQUAL: return new Node(Token.SHEQ); case NOT_TRIPLE_EQUAL: return new Node(Token.SHNE); case NOT: return new Node(Token.NOT); case POSITIVE: return new Node(Token.POS); case NEGATIVE: return new Node(Token.NEG); case TYPEOF: return new Node(Token.TYPEOF); case INSTANCEOF: return new Node(Token.INSTANCEOF); case IN: return new Node(Token.IN); case ADD: return new Node(Token.ADD); case SUBTRACT: return new Node(Token.SUB); case MULTIPLY: return new Node(Token.MUL); case DIVIDE: return new Node(Token.DIV); case MODULO: return new Node(Token.MOD); case EXPONENT: return new Node(Token.EXPONENT); case BITWISE_NOT: return new Node(Token.BITNOT); case BITWISE_OR: return new Node(Token.BITOR); case BITWISE_AND: return new Node(Token.BITAND); case BITWISE_XOR: return new Node(Token.BITXOR); case LEFT_SHIFT: return new Node(Token.LSH); case RIGHT_SHIFT: return new Node(Token.RSH); case UNSIGNED_RIGHT_SHIFT: return new Node(Token.URSH); case PRE_INCREMENT: return new Node(Token.INC); case POST_INCREMENT: Node postInc = new Node(Token.INC); postInc.putBooleanProp(Node.INCRDECR_PROP, true); return postInc; case PRE_DECREMENT: return new Node(Token.DEC); case POST_DECREMENT: Node postDec = new Node(Token.DEC); postDec.putBooleanProp(Node.INCRDECR_PROP, true); return postDec; case ASSIGN_ADD: return new Node(Token.ASSIGN_ADD); case ASSIGN_SUBTRACT: return new Node(Token.ASSIGN_SUB); case ASSIGN_MULTIPLY: return new Node(Token.ASSIGN_MUL); case ASSIGN_DIVIDE: return new Node(Token.ASSIGN_DIV); case ASSIGN_MODULO: return new Node(Token.ASSIGN_MOD); case ASSIGN_EXPONENT: return new Node(Token.ASSIGN_EXPONENT); case ASSIGN_BITWISE_OR: return new Node(Token.ASSIGN_BITOR); case ASSIGN_BITWISE_AND: return new Node(Token.ASSIGN_BITAND); case ASSIGN_BITWISE_XOR: return new Node(Token.ASSIGN_BITXOR); case ASSIGN_LEFT_SHIFT: return new Node(Token.ASSIGN_LSH); case ASSIGN_RIGHT_SHIFT: return new Node(Token.ASSIGN_RSH); case ASSIGN_UNSIGNED_RIGHT_SHIFT: return new Node(Token.ASSIGN_URSH); case YIELD: return new Node(Token.YIELD); case AWAIT: return new Node(Token.AWAIT); case DELETE: return new Node(Token.DELPROP); case TAGGED_TEMPLATELIT: return new Node(Token.TAGGED_TEMPLATELIT); case TEMPLATELIT: return new Node(Token.TEMPLATELIT); case TEMPLATELIT_SUB: return new Node(Token.TEMPLATELIT_SUB); case TEMPLATELIT_STRING: { TemplateStringValue templateStringValue = n.getTemplateStringValue(); int cookedPointer = templateStringValue.getCookedStringPointer(); String cookedString = cookedPointer == -1 ? null : this.stringPool.get(cookedPointer); String rawString = this.stringPool.get(templateStringValue.getRawStringPointer()); return Node.newTemplateLitString(cookedString, rawString); } case NEW_TARGET: return new Node(Token.NEW_TARGET); case COMPUTED_PROP: return new Node(Token.COMPUTED_PROP); case IMPORT_META: return new Node(Token.IMPORT_META); case OPTCHAIN_PROPERTY_ACCESS: return Node.newString(Token.OPTCHAIN_GETPROP, getString(n)); case OPTCHAIN_CALL: return new Node(Token.OPTCHAIN_CALL); case OPTCHAIN_ELEMENT_ACCESS: return new Node(Token.OPTCHAIN_GETELEM); case COALESCE: return new Node(Token.COALESCE); case DYNAMIC_IMPORT: return new Node(Token.DYNAMIC_IMPORT); case ASSIGN_OR: return new Node(Token.ASSIGN_OR); case ASSIGN_AND: return new Node(Token.ASSIGN_AND); case ASSIGN_COALESCE: return new Node(Token.ASSIGN_COALESCE); case EXPRESSION_STATEMENT: return new Node(Token.EXPR_RESULT); case BREAK_STATEMENT: return new Node(Token.BREAK); case CONTINUE_STATEMENT: return new Node(Token.CONTINUE); case DEBUGGER_STATEMENT: return new Node(Token.DEBUGGER); case DO_STATEMENT: return new Node(Token.DO); case FOR_STATEMENT: return new Node(Token.FOR); case FOR_IN_STATEMENT: return new Node(Token.FOR_IN); case FOR_OF_STATEMENT: return new Node(Token.FOR_OF); case FOR_AWAIT_OF_STATEMENT: return new Node(Token.FOR_AWAIT_OF); case IF_STATEMENT: return new Node(Token.IF); case RETURN_STATEMENT: return new Node(Token.RETURN); case SWITCH_STATEMENT: return new Node(Token.SWITCH); case THROW_STATEMENT: return new Node(Token.THROW); case TRY_STATEMENT: return new Node(Token.TRY); case WHILE_STATEMENT: return new Node(Token.WHILE); case EMPTY: return new Node(Token.EMPTY); case WITH: return new Node(Token.WITH); case IMPORT: return new Node(Token.IMPORT); case EXPORT: return new Node(Token.EXPORT); case VAR_DECLARATION: return new Node(Token.VAR); case CONST_DECLARATION: return new Node(Token.CONST); case LET_DECLARATION: return new Node(Token.LET); case FUNCTION_LITERAL: return new Node(Token.FUNCTION); case CLASS_LITERAL: return new Node(Token.CLASS); case BLOCK: return new Node(Token.BLOCK); case LABELED_STATEMENT: return new Node(Token.LABEL); case LABELED_NAME: return IR.labelName(getString(n)); case CLASS_MEMBERS: return new Node(Token.CLASS_MEMBERS); case METHOD_DECLARATION: return Node.newString(Token.MEMBER_FUNCTION_DEF, getString(n)); case FIELD_DECLARATION: return Node.newString(Token.MEMBER_FIELD_DEF, getString(n)); case COMPUTED_PROP_FIELD: return new Node(Token.COMPUTED_FIELD_DEF); case PARAMETER_LIST: return new Node(Token.PARAM_LIST); case RENAMABLE_STRING_KEY: return IR.stringKey(getString(n)); case QUOTED_STRING_KEY: Node quotedStringKey = IR.stringKey(getString(n)); quotedStringKey.setQuotedString(); return quotedStringKey; case CASE: return new Node(Token.CASE); case DEFAULT_CASE: return new Node(Token.DEFAULT_CASE); case CATCH: return new Node(Token.CATCH); case SUPER: return new Node(Token.SUPER); case ARRAY_PATTERN: return new Node(Token.ARRAY_PATTERN); case OBJECT_PATTERN: return new Node(Token.OBJECT_PATTERN); case DESTRUCTURING_LHS: return new Node(Token.DESTRUCTURING_LHS); case DEFAULT_VALUE: return new Node(Token.DEFAULT_VALUE); case RENAMABLE_GETTER_DEF: case QUOTED_GETTER_DEF: Node getterDef = Node.newString(Token.GETTER_DEF, getString(n)); if (n.getKind().equals(NodeKind.QUOTED_GETTER_DEF)) { getterDef.setQuotedString(); } return getterDef; case RENAMABLE_SETTER_DEF: case QUOTED_SETTER_DEF: Node setterDef = Node.newString(Token.SETTER_DEF, getString(n)); if (n.getKind().equals(NodeKind.QUOTED_SETTER_DEF)) { setterDef.setQuotedString(); } return setterDef; case IMPORT_SPECS: return new Node(Token.IMPORT_SPECS); case IMPORT_SPEC: return new Node(Token.IMPORT_SPEC); case IMPORT_STAR: return Node.newString(Token.IMPORT_STAR, getString(n)); case EXPORT_SPECS: return new Node(Token.EXPORT_SPECS); case EXPORT_SPEC: return new Node(Token.EXPORT_SPEC); case MODULE_BODY: return new Node(Token.MODULE_BODY); case ITER_REST: return new Node(Token.ITER_REST); case ITER_SPREAD: return new Node(Token.ITER_SPREAD); case OBJECT_REST: return new Node(Token.OBJECT_REST); case OBJECT_SPREAD: return new Node(Token.OBJECT_SPREAD); case NODE_KIND_UNSPECIFIED: case UNRECOGNIZED: break; } throw new IllegalStateException("Unexpected serialized kind for AstNode: " + n); } /** * If no colors are being deserialized, filters out any NodeProperty.COLOR_BEFORE_CASTs * *

This is because it doesn't make sense to have that property present on nodes that don't have * colors. */ private long filterOutCastProp(long nodeProperties) { if (colorPoolShard.isPresent()) { return nodeProperties; // we are deserializing colors, so this is fine. } return nodeProperties & ~(1L << NodeProperty.COLOR_FROM_CAST.getNumber()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy